package FxmlGUI;

import FxmlGUI.vo.FileVo;
import FxmlGUI.vo.TraverseVo;
import cn.hutool.core.util.StrUtil;
import javafx.application.Application;
import javafx.collections.ObservableMap;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Objects;


/**
 * 文件检索工具
 */
public class QueryFiles extends Application {

    private static int FOLDER_NUM = 0;

    private static int FILE_NUM = 0;
    private static String BEGIN_PATH = "./";

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        //初始化loader，定位指定的fxml文件
        FXMLLoader loader = new FXMLLoader(getClass().getResource("/Page/FxmlGUI/QueryFiles.fxml"));
        //得到一个根，就是fxml的元素群
        Parent root = loader.load();
        //获取命名空间
        ObservableMap<String, Object> nameSpace = loader.getNamespace();
        //TODO 配置
        configuration(nameSpace,primaryStage);
        //封装配置，展示
        primaryStage.setResizable(false);
        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("/Page/FxmlGUI/style.css").toExternalForm());
        primaryStage.setScene(scene);
        primaryStage.setTitle("文件检索工具 v0.1.0");
        primaryStage.show();
    }

    /**
     * 配置文件入口
     *
     * @param nameSpace 命名空间
     * @param primaryStage 主窗口
     */
    private void configuration(ObservableMap<String, Object> nameSpace, Stage primaryStage) {
        //加载目录组件
        setFolderRoot(nameSpace,primaryStage);
        //加载文件树
        setFilesTree(nameSpace, "");
        //加载计数组件
        setCntLabel(nameSpace);
        //加载搜索组件
        setQueryComponent(nameSpace);
    }

    /**
     * 配置搜索根目录元素、配置钩子方法
     * @param nameSpace 命名空间
     * @param primaryStage 主窗口
     */
    private void setFolderRoot(ObservableMap<String, Object> nameSpace, Stage primaryStage) {
        TextField filePositionInput = (TextField) nameSpace.get("filePositionInput");
        Button filelPositionBtn = (Button) nameSpace.get("filelPositionBtn");
        //给定默认地址，即当前目录
        BEGIN_PATH = Paths.get(BEGIN_PATH).normalize().toAbsolutePath().toString();
        filePositionInput.setText(BEGIN_PATH);
        //监听搜索按钮点击事件
        filelPositionBtn.setOnAction(event -> {
            // 创建一个DirectoryChooser
            DirectoryChooser directoryChooser = new DirectoryChooser();
            directoryChooser.setTitle("选择开始搜索的文件夹");
            // 设置初始路径为filePositionInput的值
            File initialDirectory = new File(filePositionInput.getText());
            if (initialDirectory.exists() && initialDirectory.isDirectory()) {
                directoryChooser.setInitialDirectory(initialDirectory);
            }
            // 显示文件夹选择对话框
            File selectedDirectory = directoryChooser.showDialog(primaryStage);
            // 处理用户选择的文件夹
            if (selectedDirectory != null) {
                BEGIN_PATH = selectedDirectory.getAbsolutePath();
                filePositionInput.setText(BEGIN_PATH);
            }
        });
        //监听文件夹输入框变化
        filePositionInput.textProperty().addListener((observable, oldValue, newValue) -> {
            BEGIN_PATH = filePositionInput.getText();
        });

    }

    /**
     * 配置文件数量显示元素
     * @param nameSpace 命名空间
     */
    private void setCntLabel(ObservableMap<String, Object> nameSpace) {
        Label cntLabel = (Label) nameSpace.get("cntLabel");
        cntLabel.setText("共计：" + FILE_NUM + "个文件，" + FOLDER_NUM + "个文件夹");
    }

    /**
     * 配置搜索文件的元素、配置钩子方法
     * @param nameSpace 命名空间
     */
    private void setQueryComponent(ObservableMap<String, Object> nameSpace) {
        TextField queryInput = (TextField) nameSpace.get("queryInput");
        Button queryBtn = (Button) nameSpace.get("queryBtn");
        // 创建一个加载指示器（弃用）
        Label loadingTree = (Label) nameSpace.get("loadingTree");
        loadingTree.setVisible(false);
        //监听搜索按钮点击事件
        queryBtn.setOnAction(event -> {
            loadingTree(nameSpace,queryInput,loadingTree);
        });
        //监听搜索输入框回车事件
        queryInput.setOnKeyPressed(event -> {
            if(event.getCode() == KeyCode.ENTER){
                loadingTree(nameSpace,queryInput,loadingTree);
            }
        });

    }

    /**
     * 加载文件树
     * @param nameSpace 命名空间
     * @param queryInput 搜索框
     * @param loadingTree loading组件（弃用）
     */
    void loadingTree(ObservableMap<String, Object> nameSpace,TextField queryInput,Label loadingTree){
        if (StrUtil.isBlank(queryInput.getText())) {
            showMessage("提示","搜索内容不能为空", Alert.AlertType.INFORMATION);
            return;
        }
        FOLDER_NUM = 0;
        FILE_NUM = 0;
        setFilesTree(nameSpace, queryInput.getText());
        setCntLabel(nameSpace);
        // 显示加载指示器
//        loadingTree.setVisible(true);
//        new Thread(() -> {// 2
//            try {
//                FOLDER_NUM = 0;
//                FILE_NUM = 0;
//                setFilesTree(nameSpace, queryInput.getText());
//                setCntLabel(nameSpace);
//            } catch (Exception e) {
//                e.printStackTrace();// 3
//            } finally {
//                loadingTree.setVisible(false);// 4
//            }
//        }).start();
    }


    /**
     * 具体遍历方案-递归[主]
     * @param nameSpace 命名空间
     * @param key 关键词
     */
    private void setFilesTree(ObservableMap<String, Object> nameSpace, String key) {
        File file = new File(BEGIN_PATH);
        if (!file.exists() || !file.isDirectory()){
            showMessage("提示","你输入或选择的目录不存在", Alert.AlertType.INFORMATION);
        }
        TreeView<FileVo> filesTree = (TreeView<FileVo>) nameSpace.get("filesTree");
        TreeItem<FileVo> rootItem = new TreeItem<>(new FileVo(file.getName(), file.getAbsolutePath()));
        rootItem.setExpanded(true);
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    if (f.isDirectory()) {
                        TraverseVo traverseVo = traverseFolder(f, key);
                        if (traverseVo.isHit()) {
                            rootItem.getChildren().add(traverseVo.getTreeItem());
                        }
                        if (StrUtil.isNotBlank(key) && file.getName().contains(key)) {
                            FOLDER_NUM++;
                            rootItem.getChildren().add(new TreeItem<>(new FileVo(file.getName(), file.getAbsolutePath()), getFolderIcon()));
                        }
                        continue;
                    }
                    if (StrUtil.isNotBlank(key) && f.getName().contains(key)) {
                        FILE_NUM++;
                        rootItem.getChildren().add(new TreeItem<>(new FileVo(f.getName(), f.getAbsolutePath())));
                    }
                }
            }
        }
        filesTree.setCellFactory(tv -> {
            TreeCell<FileVo> cell = new TreeCell<FileVo>() {
                @Override
                protected void updateItem(FileVo item, boolean empty) {
                    super.updateItem(item, empty);
                    if (empty) {
                        setText(null);
                        setGraphic(null);
                    } else {
                        Label label = new Label(item.getName());
                        label.setOnMouseClicked(event -> {
                            if (event.getClickCount() == 2) { // 双击事件
                                File file = new File(item.getPath());
                                // 打开文件
                                try {
                                    Desktop.getDesktop().open(file);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                        HBox hBox = new HBox(5); // 5 is the spacing between icon and label
                        // get the icon from the TreeItem
                        if (getTreeItem().getGraphic() != null) {
                            hBox.getChildren().add(getTreeItem().getGraphic());
                        }
                        hBox.getChildren().add(label);
                        setGraphic(hBox);
                    }
                }
            };
            return cell;
        });


        filesTree.getStyleClass().add("custom-cursor");
        filesTree.setShowRoot(false);
        filesTree.setRoot(rootItem);
    }

    /**
     * 具体遍历方案-递归[从]
     * @param key 关键词
     */
    private TraverseVo traverseFolder(File folder, String key) {
        boolean isHit = false;
        TreeItem<FileVo> parent = new TreeItem<>(new FileVo(folder.getName(), folder.getAbsolutePath()), getFolderIcon());
        parent.setExpanded(true);
        for (File file : Objects.requireNonNull(folder.listFiles())) {
            if (file.isDirectory()) {
                TraverseVo traverseVo = traverseFolder(file, key);
                if (traverseVo.isHit()) {
                    isHit = true;
                    parent.getChildren().add(traverseVo.getTreeItem());
                }
                if (StrUtil.isNotBlank(key) && file.getName().contains(key)) {
                    isHit = true;
                    FOLDER_NUM++;
                    parent.getChildren().add(new TreeItem<>(new FileVo(file.getName(), file.getAbsolutePath()), getFolderIcon()));
                }
                continue;
            }
            if (StrUtil.isNotBlank(key) && file.getName().contains(key)) {
                isHit = true;
                FILE_NUM++;
                parent.getChildren().add(new TreeItem<>(new FileVo(file.getName(), file.getAbsolutePath())));
            }
        }
        return new TraverseVo(parent, isHit);
    }

    /**
     * 获取文件夹图标
     * <p>
     * 在 JavaFX 中， ANode只能Parent同时包含在单个元素中。
     * 因此，您应该将Images 存储为成员变量，并ImageView为每个变量创建一个新变量TreeItem，而不是重用它
     * 祥见：https://stackoverflow.com/questions/28019094/in-javafx-treeview-how-to-set-icons-for-all-the-items
     *
     * @return
     */
    Node getFolderIcon() {
        Node rootIcon;
        ImageView imageView = new ImageView(
                new Image(Objects.requireNonNull(QueryFiles.class.getResourceAsStream("/Image/folder.png")))
        );
        imageView.setFitWidth(16);
        imageView.setFitHeight(16);
        //文件夹图标
        rootIcon = imageView;
        return rootIcon;
    }

    /**
     * 弹窗渲染
     * @param title 标题
     * @param content 内容
     * @param type 类型
     */
    void showMessage(String title, String content, Alert.AlertType type){
        VBox root = new VBox(10);
        root.setMinSize(200, 100);
        // 创建一个信息弹窗
        Alert alert = new Alert(type);
        alert.setTitle(title);
        alert.setHeaderText(null);
        alert.setContentText(content);
        // 显示弹窗
        alert.showAndWait();
        return;
    }
}
